

package com.hazelcast.internal.dynamicconfig;

import com.hazelcast.config.*;
import com.hazelcast.config.vector.VectorCollectionConfig;
import com.hazelcast.internal.util.XmlUtil;
import org.snakeyaml.engine.v2.api.Dump;
import org.snakeyaml.engine.v2.api.DumpSettings;
import org.snakeyaml.engine.v2.common.FlowStyle;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;

import static com.hazelcast.internal.config.ConfigSections.*;

public final class DynamicConfigGeneratorUtil {

    private DynamicConfigGeneratorUtil() {
        // not called
    }

    public static String licenseKeyConfigGenerator(String subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, null, Config::setLicenseKey, DynamicConfigXmlGenerator::licenseKeyXmlGenerator, DynamicConfigYamlGenerator::licenseKeyYamlGenerator);
    }

    public static String wanReplicationConfigGenerator(WanReplicationConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, WAN_REPLICATION.getName(), Config::addWanReplicationConfig, DynamicConfigXmlGenerator::wanReplicationXmlGenerator, DynamicConfigYamlGenerator::wanReplicationYamlGenerator);
    }

    public static String mapConfigGenerator(MapConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, MAP.getName(), Config::addMapConfig, DynamicConfigXmlGenerator::mapXmlGenerator, DynamicConfigYamlGenerator::mapYamlGenerator);
    }

    public static String cacheConfigGenerator(CacheSimpleConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, CACHE.getName(), Config::addCacheConfig, DynamicConfigXmlGenerator::cacheXmlGenerator, DynamicConfigYamlGenerator::cacheYamlGenerator);
    }

    public static String queueConfigGenerator(QueueConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, QUEUE.getName(), Config::addQueueConfig, DynamicConfigXmlGenerator::queueXmlGenerator, DynamicConfigYamlGenerator::queueYamlGenerator);
    }

    public static String listConfigGenerator(ListConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, LIST.getName(), Config::addListConfig, DynamicConfigXmlGenerator::listXmlGenerator, DynamicConfigYamlGenerator::listYamlGenerator);
    }

    public static String setConfigGenerator(SetConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, SET.getName(), Config::addSetConfig, DynamicConfigXmlGenerator::setXmlGenerator, DynamicConfigYamlGenerator::setYamlGenerator);
    }

    public static String multiMapConfigGenerator(MultiMapConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, MULTIMAP.getName(), Config::addMultiMapConfig, DynamicConfigXmlGenerator::multiMapXmlGenerator, DynamicConfigYamlGenerator::multiMapYamlGenerator);
    }

    public static String replicatedMapConfigGenerator(ReplicatedMapConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, REPLICATED_MAP.getName(), Config::addReplicatedMapConfig, DynamicConfigXmlGenerator::replicatedMapXmlGenerator, DynamicConfigYamlGenerator::replicatedMapYamlGenerator);
    }

    public static String ringbufferConfigGenerator(RingbufferConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, RINGBUFFER.getName(), Config::addRingBufferConfig, DynamicConfigXmlGenerator::ringbufferXmlGenerator, DynamicConfigYamlGenerator::ringbufferYamlGenerator);
    }

    public static String topicConfigGenerator(TopicConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, TOPIC.getName(), Config::addTopicConfig, DynamicConfigXmlGenerator::topicXmlGenerator, DynamicConfigYamlGenerator::topicYamlGenerator);
    }

    public static String reliableTopicConfigGenerator(ReliableTopicConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, RELIABLE_TOPIC.getName(), Config::addReliableTopicConfig, DynamicConfigXmlGenerator::reliableTopicXmlGenerator, DynamicConfigYamlGenerator::reliableTopicYamlGenerator);
    }

    public static String executorConfigGenerator(ExecutorConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, EXECUTOR_SERVICE.getName(), Config::addExecutorConfig, DynamicConfigXmlGenerator::executorXmlGenerator, DynamicConfigYamlGenerator::executorYamlGenerator);
    }

    public static String durableExecutorConfigGenerator(DurableExecutorConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, DURABLE_EXECUTOR_SERVICE.getName(), Config::addDurableExecutorConfig, DynamicConfigXmlGenerator::durableExecutorXmlGenerator, DynamicConfigYamlGenerator::durableExecutorYamlGenerator);
    }

    public static String scheduledExecutorConfigGenerator(ScheduledExecutorConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, SCHEDULED_EXECUTOR_SERVICE.getName(), Config::addScheduledExecutorConfig, DynamicConfigXmlGenerator::scheduledExecutorXmlGenerator, DynamicConfigYamlGenerator::scheduledExecutorYamlGenerator);
    }

    public static String cardinalityEstimatorConfigGenerator(CardinalityEstimatorConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, CARDINALITY_ESTIMATOR.getName(), Config::addCardinalityEstimatorConfig, DynamicConfigXmlGenerator::cardinalityEstimatorXmlGenerator, DynamicConfigYamlGenerator::cardinalityEstimatorYamlGenerator);
    }

    public static String flakeIdGeneratorConfigGenerator(FlakeIdGeneratorConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, FLAKE_ID_GENERATOR.getName(), Config::addFlakeIdGeneratorConfig, DynamicConfigXmlGenerator::flakeIdGeneratorXmlGenerator, DynamicConfigYamlGenerator::flakeIdGeneratorYamlGenerator);
    }

    public static String pnCounterConfigGenerator(PNCounterConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, PN_COUNTER.getName(), Config::addPNCounterConfig, DynamicConfigXmlGenerator::pnCounterXmlGenerator, DynamicConfigYamlGenerator::pnCounterYamlGenerator);
    }

    public static String dataConnectionConfigGenerator(DataConnectionConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, DATA_CONNECTION.getName(), Config::addDataConnectionConfig, DynamicConfigXmlGenerator::dataConnectionXmlGenerator, DynamicConfigYamlGenerator::dataConnectionYamlGenerator);
    }

    public static String tcpIpConfigGenerator(TcpIpConfig subConfig, boolean configIsXml, int indent, boolean advancedNetwork) {
        return configGenerator(subConfig, configIsXml, indent, TCP_IP.getName(), (config, tcpIpConfig) -> {
            if (advancedNetwork) {
                config.getAdvancedNetworkConfig().getJoin().setTcpIpConfig(tcpIpConfig);
            } else {
                config.getNetworkConfig().getJoin().setTcpIpConfig(tcpIpConfig);
            }
        }, DynamicConfigXmlGenerator::tcpIpConfigXmlGenerator, DynamicConfigYamlGenerator::tcpIpConfigYamlGenerator);
    }

    public static String userCodeNamespaceConfigGenerator(UserCodeNamespaceConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, null, (config, userCodeNamespaceConfig) -> {
            // assumes namespaces are already enabled declaratively
            config.getNamespacesConfig().setEnabled(true);
            config.getNamespacesConfig().addNamespaceConfig(userCodeNamespaceConfig);
        }, ConfigXmlGenerator::namespaceConfigurations, DynamicConfigYamlGenerator::namespaceConfigGenerator);
    }

    public static String vectorCollectionConfigGenerator(VectorCollectionConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, VECTOR.getName(), Config::addVectorCollectionConfig, DynamicConfigXmlGenerator::vectorCollectionXmlGenerator, DynamicConfigYamlGenerator::vectorCollectionYamlGenerator);
    }

    public static String diagnosticsConfigGenerator(DiagnosticsConfig subConfig, boolean configIsXml, int indent) {
        return configGenerator(subConfig, configIsXml, indent, null, Config::setDiagnosticsConfig, DynamicConfigXmlGenerator::diagnosticsXmlGenerator, DynamicConfigYamlGenerator::diagnosticsYamlGenerator);
    }

    private static <T> String configGenerator(T subConfig, boolean configIsXml, int indent, String subConfigTypeName, BiConsumer<Config, T> subConfigAdder, BiConsumer<ConfigXmlGenerator.XmlGenerator, Config> xmlConfigGenerator, BiConsumer<Map<String, Object>, Config> yamlConfigGenerator) {

        String subConfigAsString;

        Config config = new Config();
        subConfigAdder.accept(config, subConfig);

        if (configIsXml) {
            StringBuilder xml = new StringBuilder();
            ConfigXmlGenerator.XmlGenerator gen = new ConfigXmlGenerator.XmlGenerator(xml);

            xmlConfigGenerator.accept(gen, config);

            subConfigAsString = xml.toString();
            subConfigAsString = XmlUtil.format(subConfigAsString, indent);
        } else {
            Map<String, Object> yaml = new LinkedHashMap<>();

            yamlConfigGenerator.accept(yaml, config);

            DumpSettings dumpSettings = DumpSettings.builder().setDefaultFlowStyle(FlowStyle.BLOCK).setIndicatorIndent(indent - 2).setIndent(indent).build();
            Dump dump = new Dump(dumpSettings);
            subConfigAsString = dump.dumpToString(subConfigTypeName == null ? yaml : yaml.get(subConfigTypeName));
        }

        return subConfigAsString;
    }
}
